<?php

namespace App\Utility;

use App\Utility\Pool\RedisPool;
use EasySwoole\Component\Pool\PoolManager;
use EasySwoole\EasySwoole\Config;

class Cache{

    protected $redis;
    protected $options;

    public function __construct(){
        $options = Config::getInstance()->getConf('redis');
        $redis = PoolManager::getInstance()->getPool(RedisPool::class)->getObj($options['pool_time_out']);
        if ($redis) {
            $this->redis = $redis;
            $this->options = $options;
        } else {
            //直接抛给异常处理，不往下
            throw new \Exception('error,Redis Pool is Empty');
        }
    }

    public function __destruct(){
        PoolManager::getInstance()->getPool(RedisPool::class)->recycleObj($this->redis);
    }

    /**
     * 获取指定 key 的值。
     * @param $name
     * @param null $default
     * @return null
     */
    public function get($name, $default = null){
        $key   = $this->getCacheKey($name);
        $value = $this->redis->get($key);
        if (is_null($value) || false === $value) {
            return $default;
        }
        return is_numeric($value) ? $value : $this->unPackData($value);
    }

    /**
     * 设置指定 key 的值
     * @param $name
     * @param $value
     * @param null $expire
     * @return mixed
     */
    public function set($name, $value, $expire = null){
        if (is_null($expire)) {
            $expire = $this->options['expire'];
        }
        $key   = $this->getCacheKey($name);
        $expire= $this->getExpireTime($expire);
        $value = is_numeric($value) ? $value : $this->packData($value);
        if ($expire) {
            $result = $this->redis->setex($key, $expire, $value);
        } else {
            $result = $this->redis->set($key, $value);
        }
        return $result;
    }


    /**
     * 将 key 所储存的值加上给定的增量值
     * @param $name
     * @param int $step
     * @return mixed
     */
    public function inc($name, $step = 1){
        return $this->redis->incrBy($this->getCacheKey($name), $step);
    }

    /**
     * key 所储存的值减去给定的减量值
     * @param $name
     * @param int $step
     * @return mixed
     */
    public function dec($name, $step = 1){
        return $this->redis->decrBy($this->getCacheKey($name), $step);
    }

    /**
     * 获取指定 key 的值并删除key。
     * @param $name
     * @param null $default
     * @return null
     */
    public function pull($name){
        $result = $this->get($name);
        if ($result) {
            $this->delete($name);
            return $result;
        }
        return null;
    }

    /**
     * 如果key设置一个值
     * @param $name
     * @param $value
     * @param null $expire
     * @return mixed|null
     */
    public function remember($name, $value, $expire = null){
        if (!$this->has($name)) {
            return $this->set($name, $value, $expire);
        }
        return $this->get($name);
    }


    /**
     * 删除指定key。
     * @param $name
     * @return mixed
     */
    public function delete($name){
        return $this->redis->delete($this->getCacheKey($name));
    }

    /**
     * 检查给定 key 是否存在
     * @param $name
     * @return mixed
     */
    public function has($name){
        return $this->redis->exists($this->getCacheKey($name));
    }

    /**
     * 清除所有缓存
     * @return bool
     */
    public function clear(){
        $keys = $this->redis->keys($this->options['prefix'].'*');
        if ($keys) {
            $this->redis->del(...$keys);
        }
        return true;
    }

    /**
     * 获取缓存前缀
     * @param $name
     * @return string
     */
    protected function getCacheKey($name){
        return $this->options['prefix'].$name;
    }

    /**
     * 过期时间
     * @param $expire
     * @return int
     */
    protected function getExpireTime($expire){
        if ($expire instanceof \DateTime) {
            $expire = $expire->getTimestamp() - time();
        }
        return (int) $expire;
    }

    /**
     * 序列化数据
     * @param $data
     * @return string
     */
    protected function packData($data){
        return serialize($data);
    }

    /**
     * 反序列化数据
     * @param $data
     * @return mixed
     */
    protected function unPackData($data){
        return unserialize($data);
    }

}
